﻿using System;
using System.Collections.Generic;

namespace TekaMUD.Engine.Blocks
{
	[Serializable()]
	public class World
	{
		[NonSerialized]
		private static World _instance;
		[NonSerialized]
		private object SyncRoot = new object();
		

		private Dictionary<string, Continent> Continents;
		private Dictionary<string, Player> Players;
		private Dictionary<Room, List<Player>> Positions_Players;
		private Dictionary<Room, List<GameObject>> Positions_Objects;
		private volatile Dictionary<Room, List<NPC>> Positions_NPCs;


		private World()
		{
			Continents = new Dictionary<string, Continent>();
			Players = new Dictionary<string, Player>();
			Positions_Players = new Dictionary<Room, List<Player>>();
			Positions_NPCs = new Dictionary<Room, List<NPC>>();
		}

		static World()
		{
			_instance = new World();
		}

		public static World Current
		{
			get
			{
				return _instance;
			}
		}

		public void AddContinent(Continent c)
		{
			lock (SyncRoot)
			{
				if (Continents.ContainsKey(c.Name))
					throw new UserException("Continent " + c.Name + " already exists.");

				Continents.Add(c.Name, c);
			}
		}

		public Continent[] GetAllContinents()
		{
			lock (SyncRoot)
			{
				Continent[] ret = new Continent[Continents.Count];
				Continents.Values.CopyTo(ret, 0);

				return ret;
			}
		}

		public Continent GetContinent(string name)
		{
			lock (SyncRoot)
			{
				Continent c;

				if (Continents.TryGetValue(name, out c))
					return c;
				else
					return null;
			}
		}

		public Player[] GetPlayersByRoom(Room r)
		{
			Player[] ret;

			lock (SyncRoot)
			{
				if (Positions_Players.ContainsKey(r))
					ret = Positions_Players[r].ToArray();
				else
					ret = new Player[] { };

			}

			return ret;
		}

		public void AssignPlayerToRoom(Room r, Player p)
		{
			lock (SyncRoot)
			{
				if(p.CurrentRoom != null && Positions_Players.ContainsKey(p.CurrentRoom))
					Positions_Players[p.CurrentRoom].Remove(p);

				List<Player> list;

				if (Positions_Players.TryGetValue(r, out list))
					list.Add(p);
				else
					Positions_Players[r] = new List<Player>(new Player[] { p });
				
				p.CurrentRoom = r;
					 
			}
		}

		public void AssignNpcToRoom(NPC npc, Room r)
		{
			lock (SyncRoot)
			{
				if (npc.CurrentRoom != null && Positions_NPCs.ContainsKey(npc.CurrentRoom))
					Positions_NPCs[npc.CurrentRoom].Remove(npc);
				
				List<NPC> list;

				if (Positions_NPCs.TryGetValue(r, out list))
					list.Add(npc);
				else
					Positions_NPCs[r] = new List<NPC>(new NPC[] { npc });

				npc.CurrentRoom = r;
			}
		}

		public static void LoadWorld(World newWorld)
		{
			Current.Continents = newWorld.Continents;
		}

		public NPC[] GetNpcByRoom(Room r)
		{
			NPC[] ret;

			lock (SyncRoot)
			{
				if (Positions_NPCs.ContainsKey(r))
					ret = Positions_NPCs[r].ToArray();
				else
					ret = new NPC[] { };
			}

			return ret;
		}
	}
}
